package org.stone.project.redisson.config;

import cn.hutool.core.util.ObjectUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.codec.JsonJacksonCodec;
import org.redisson.config.*;
import org.redisson.spring.starter.RedissonAutoConfiguration;
import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer;
import org.redisson.spring.starter.RedissonProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.core.io.Resource;
import org.springframework.util.ReflectionUtils;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

@Slf4j
@Configuration
public class RedissonConfig {
    @Autowired
    private RedissonProperties redissonProperties;
    @Autowired
    private RedisProperties redisProperties;
    @Autowired(
            required = false
    )
    private List<RedissonAutoConfigurationCustomizer> redissonAutoConfigurationCustomizers;
    @Autowired
    private ApplicationContext ctx;

    @Bean
    @ConditionalOnMissingBean
    public ObjectMapper objectMapper(){
        log.warn("非mvc的objectMapper序列化，注入到redisson中");
        return new ObjectMapper();
    }

    /**
     * 覆盖原有的redissonClient
     * @return
     */
    @Bean(
            destroyMethod = "shutdown"
    )
    @ConditionalOnMissingBean({RedissonClient.class})
    public RedissonClient redisson() throws IOException {
        Method clusterMethod = ReflectionUtils.findMethod(RedisProperties.class, "getCluster");
        Method usernameMethod = ReflectionUtils.findMethod(RedisProperties.class, "getUsername");
        Method timeoutMethod = ReflectionUtils.findMethod(RedisProperties.class, "getTimeout");
        Method connectTimeoutMethod = ReflectionUtils.findMethod(RedisProperties.class, "getConnectTimeout");
        Method clientNameMethod = ReflectionUtils.findMethod(RedisProperties.class, "getClientName");
        Object timeoutValue = ReflectionUtils.invokeMethod(timeoutMethod, this.redisProperties);
        Integer timeout = null;
        if (timeoutValue instanceof Duration) {
            timeout = (int)((Duration)timeoutValue).toMillis();
        } else if (timeoutValue != null) {
            timeout = (Integer)timeoutValue;
        }

        Integer connectTimeout = null;
        if (connectTimeoutMethod != null) {
            Object connectTimeoutValue = ReflectionUtils.invokeMethod(connectTimeoutMethod, this.redisProperties);
            if (connectTimeoutValue != null) {
                connectTimeout = (int)((Duration)connectTimeoutValue).toMillis();
            }
        } else {
            connectTimeout = timeout;
        }

        String clientName = null;
        if (clientNameMethod != null) {
            clientName = (String)ReflectionUtils.invokeMethod(clientNameMethod, this.redisProperties);
        }

        String username = null;
        if (usernameMethod != null) {
            username = (String)ReflectionUtils.invokeMethod(usernameMethod, this.redisProperties);
        }

        Config config;
        if (this.redissonProperties.getConfig() != null) {
            try {
                config = Config.fromYAML(this.redissonProperties.getConfig());
            } catch (IOException var20) {
                try {
                    config = Config.fromJSON(this.redissonProperties.getConfig());
                } catch (IOException var19) {
                    var19.addSuppressed(var20);
                    throw new IllegalArgumentException("Can't parse config", var19);
                }
            }
        } else if (this.redissonProperties.getFile() != null) {
            try {
                InputStream is = this.getConfigStream();
                config = Config.fromYAML(is);
            } catch (IOException var18) {
                try {
                    InputStream is = this.getConfigStream();
                    config = Config.fromJSON(is);
                } catch (IOException var17) {
                    var17.addSuppressed(var18);
                    throw new IllegalArgumentException("Can't parse config", var17);
                }
            }
        } else if (this.redisProperties.getSentinel() != null) {
            Method nodesMethod = ReflectionUtils.findMethod(RedisProperties.Sentinel.class, "getNodes");
            Object nodesValue = ReflectionUtils.invokeMethod(nodesMethod, this.redisProperties.getSentinel());
            String[] nodes;
            if (nodesValue instanceof String) {
                nodes = this.convert(Arrays.asList(((String)nodesValue).split(",")));
            } else {
                nodes = this.convert((List)nodesValue);
            }

            config = new Config();
            SentinelServersConfig c = (SentinelServersConfig)((SentinelServersConfig)((SentinelServersConfig)config.useSentinelServers().setMasterName(this.redisProperties.getSentinel().getMaster()).addSentinelAddress(nodes).setDatabase(this.redisProperties.getDatabase()).setUsername(username)).setPassword(this.redisProperties.getPassword())).setClientName(clientName);
            if (connectTimeout != null) {
                c.setConnectTimeout(connectTimeout);
            }

            if (connectTimeoutMethod != null && timeout != null) {
                c.setTimeout(timeout);
            }
        } else {
            Method method;
            if (clusterMethod != null && ReflectionUtils.invokeMethod(clusterMethod, this.redisProperties) != null) {
                Object clusterObject = ReflectionUtils.invokeMethod(clusterMethod, this.redisProperties);
                method = ReflectionUtils.findMethod(clusterObject.getClass(), "getNodes");
                List<String> nodesObject = (List)ReflectionUtils.invokeMethod(method, clusterObject);
                String[] nodes = this.convert(nodesObject);
                config = new Config();
                ClusterServersConfig c = (ClusterServersConfig)((ClusterServersConfig)((ClusterServersConfig)config.useClusterServers().addNodeAddress(nodes).setUsername(username)).setPassword(this.redisProperties.getPassword())).setClientName(clientName);
                if (connectTimeout != null) {
                    c.setConnectTimeout(connectTimeout);
                }

                if (connectTimeoutMethod != null && timeout != null) {
                    c.setTimeout(timeout);
                }
            } else {
                config = new Config();
                String prefix = "redis://";
                method = ReflectionUtils.findMethod(RedisProperties.class, "isSsl");
                if (method != null && (Boolean)ReflectionUtils.invokeMethod(method, this.redisProperties)) {
                    prefix = "rediss://";
                }

                SingleServerConfig c = (SingleServerConfig)((SingleServerConfig)((SingleServerConfig)config.useSingleServer().setAddress(prefix + this.redisProperties.getHost() + ":" + this.redisProperties.getPort()).setDatabase(this.redisProperties.getDatabase()).setUsername(username)).setPassword(this.redisProperties.getPassword())).setClientName(clientName);
                if (connectTimeout != null) {
                    c.setConnectTimeout(connectTimeout);
                }

                if (connectTimeoutMethod != null && timeout != null) {
                    c.setTimeout(timeout);
                }
            }
        }

        if (this.redissonAutoConfigurationCustomizers != null) {
            Iterator var27 = this.redissonAutoConfigurationCustomizers.iterator();

            while(var27.hasNext()) {
                RedissonAutoConfigurationCustomizer customizer = (RedissonAutoConfigurationCustomizer)var27.next();
                customizer.customize(config);
            }
        }

        config.setCodec(new JsonJacksonCodec(objectMapper()));

        return Redisson.create(config);
    }

    private InputStream getConfigStream() throws IOException {
        Resource resource = this.ctx.getResource(this.redissonProperties.getFile());
        return resource.getInputStream();
    }

    private String[] convert(List<String> nodesObject) {
        List<String> nodes = new ArrayList(nodesObject.size());
        Iterator var3 = nodesObject.iterator();

        while(true) {
            while(var3.hasNext()) {
                String node = (String)var3.next();
                if (!node.startsWith("redis://") && !node.startsWith("rediss://")) {
                    nodes.add("redis://" + node);
                } else {
                    nodes.add(node);
                }
            }

            return (String[])nodes.toArray(new String[0]);
        }
    }
}
